Ninja Brawler

Copyright Disclaimer: None of the art assets belong to me. MegaMan and Ryu are both properties of Capcom and I do not claim any ownership for these assets. They are used strictly for educational purposes in my Game 330 class at George Mason University.

About The Game

     Ninja Brawler was a first attempt at creating a basic 2d multiplayer fighting system. None of the artwork is original and belongs to their respective owners. I made Ninja Brawler for my platform analysis class at George Mason University as a proof of concept piece in the unity game engine.

Programming Overview

     Ninja Brawler uses unity's animation system to control the hitbox placements and automate values such as damage, stun, launch direction and magnitude. Because animations contained all of the iframe, hitbox, and hit values, there was no longer a need for a class based combat system. The character controller would just trigger the animations as long as the player should be able to perform the move.

     The player input is abstracted from the character output. This is done to allow for intellegent combo responses, and move queueing. My favorite part about abstracting the input, is that it allows for clearing queued moves in response to taking damage with ease. This also prevents intentional move cancelling and handles end lag for moves.

using UnityEngine; using System.Collections; using UnityEngine.SceneManagement; public class ryuController : MonoBehaviour { public Animator anim; public CharacterStats stats; string fightQueue; Vector2 Position2D; public Animator VictAnim; public Rigidbody2D pRig; public string forward; public string back; public string idle; public string basicStrike, basicStrike2, basicStrike3, smash, special; [SerializeField] public string Jump = "Jump", BasicAttack = "Fire1", SpecialAttack = "Fire2", Guard = "Fire3", Horizontal = "Horizontal"; public string attackString; public bool Hurting; public bool again; private float speed = 0.1f; public int jumpCount; public float lag; public LayerMask mask; public MoveStats otherhit; public AudioSource aud; public AudioClip attackClip, hitClip; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Mathf.Abs(Input.GetAxis (Horizontal)) > 0.2f && Mathf.Abs(speed) < 0.2f) { speed =Mathf.Clamp(Mathf.Abs(Input.GetAxis (Horizontal) * speed + Input.GetAxis(Horizontal)/4.9f), 0.21f, 1); } else { speed =Mathf.Abs(Input.GetAxis (Horizontal) * speed); } if (stats.health > 0) { anim.speed = 1; if (!Hurting) { if (GroundCheck ()) { lag -= Time.deltaTime; jumpCount = 2; tag = "Untagged"; anim.SetBool ("Jump", false); anim.SetBool ("Block", false); if (lag <= 0) { if (attackString == "" && Input.GetButton (Guard)) { anim.SetBool ("Block", true); anim.Play ("Block"); tag = "Blocking"; } else if (Input.GetButtonDown (BasicAttack)) { anim.SetFloat ("Charge", 0); attackString += "a"; StartCoroutine (StringClearer ()); } else if (Input.GetButtonDown (SpecialAttack)) { attackString += "b"; StartCoroutine (StringClearer ()); } else if (Input.GetButtonUp (BasicAttack)) { if (attackString.Length != 0) { if (attackString [0] == 'a' && !anim.GetBool ("Basic")) { anim.Play (basicStrike); anim.SetBool ("Basic", true); } } else if (!again) { anim.SetBool ("IsHolding", false); anim.Play ("Finish"); lag = anim.GetFloat ("Charge") / 6; } again = false; } else if (attackString.Length > 0) { if (Input.GetButton (SpecialAttack) && attackString [0] == 'b' && !anim.GetBool ("Special") && attackString.Length != 0) { anim.Play (special); anim.SetBool ("Special", true); } }else if (Input.GetButton (BasicAttack) && attackString == "" && !again && again == false) { if (anim.GetFloat ("Charge") == 0f) { anim.Play (smash); } anim.SetFloat ("Charge", (anim.GetFloat ("Charge") + Time.deltaTime)); anim.SetBool ("IsHolding", true); if (anim.GetFloat ("Charge") > 3 && anim.GetBool ("IsHolding") == true) { anim.Play ("Finish"); anim.SetBool ("IsHolding", false); lag = anim.GetFloat ("Charge")/6; again = true; } } else if (Input.GetButtonDown (Jump)) { anim.speed = 1; jumpCount = 1; if (speed > 0.2f || speed < -0.2f) { anim.Play ("JumpForward"); } else { anim.Play ("Jump"); } anim.SetBool ("Jump", true); } else if (Input.GetAxis(Horizontal)< -0.2f) { speed += Time.deltaTime; transform.rotation = Quaternion.Euler (Vector3.up * -180); anim.SetFloat ("Speed", Mathf.Abs (speed)); anim.speed = stats.speed; } else if (Input.GetAxis(Horizontal)> 0.2f) { speed += Time.deltaTime; transform.rotation = Quaternion.Euler (Vector3.zero); anim.SetFloat ("Speed", Mathf.Abs (speed)); anim.speed = stats.speed; } else { anim.SetFloat ("Speed", speed); } } anim.SetBool ("IsAirborne", false); } else { if (Input.GetButtonUp (Guard)) { anim.SetBool ("Block", false); } pRig.AddForce (Vector2.right * Input.GetAxis (Horizontal)*200f*(2 + stats.speed/3)); anim.SetBool ("IsAirborne", true); if (!anim.GetBool ("Jump")) { if (Input.GetButtonDown (Jump) && jumpCount >= 1) { anim.SetTrigger ("JumpT"); jumpCount -= 2; } } } } } } IEnumerator StringClearer(){ yield return new WaitForSeconds (0.3f); if (attackString.Length > 1) { if (attackString [1] != 'a') { anim.SetBool ("Basic", false); } else if (attackString [1] != 'b') { anim.SetBool ("Special", false); } } else { anim.SetBool ("Basic", false); anim.SetBool ("Special", false); } if (attackString != "") { attackString = attackString.Substring (1); } } void OnTriggerEnter2D(Collider2D other){ if (other.GetComponent() && tag != "Blocking" && other.tag != stats.player) { MoveStats hit = other.GetComponent (); StartCoroutine (HitRoutine (hit)); } } public IEnumerator ResetPhysics(){ Vector2 newVel = pRig.velocity; newVel.y = 0; pRig.velocity = newVel; yield return null; } IEnumerator HitRoutine(MoveStats hitInfo){ attackString = ""; anim.SetFloat ("Charge", 0); anim.SetBool ("Special", false); anim.SetBool ("Basic", false); stats.DealDamage(hitInfo.damage); if (stats.health < 1) { StartCoroutine (MainMenu ()); } anim.SetBool ("Hurt", true); anim.Play ("Hurt"); yield return new WaitForSeconds (hitInfo.stun); hitInfo.direction.x *= (transform.position.x - hitInfo.transform.position.x); while (hitInfo.knockBack > 0) { pRig.AddForce (hitInfo.direction * hitInfo.knockBack*2); hitInfo.knockBack--; yield return new WaitForSeconds (0.02f); } yield return new WaitUntil(() => GroundCheck()); anim.SetBool ("Hurt", false); if(stats.health <= 0){ VictAnim.enabled = true; VictAnim.Play ("Victory!!"); StartCoroutine (MainMenu ()); transform.rotation = Quaternion.Euler (Vector3.up * -180); anim.Play ("Death"); } } public IEnumerator MainMenu(){ yield return new WaitForSeconds (7f); SceneManager.LoadScene ("Wrapper"); } bool GroundCheck(){ Position2D.x = transform.position.x; Position2D.y = transform.position.y; if (Physics2D.Raycast (Position2D, Vector2.down, 0.1f, mask)) { return true; } else { return false; } } }
Download

Project Files

file size: 80.8 Mb